package com.ebupt.migu.music.schedule;


import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ebupt.migu.music.common.constants.UserGroupConstants;
import com.ebupt.migu.music.common.utils.ESearch;
import com.ebupt.migu.music.common.utils.FileRW;
import com.ebupt.migu.music.common.utils.PatternVer;
import com.ebupt.migu.music.common.utils.UpdateUserGroupSize;
import com.ebupt.migu.music.userGroup.mapper.NewCrowdMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 文件及FTP导入创建人群
 *
 * @author liuyangyang@cd.ebupt.com
 */
@Async
@Slf4j
@Component
@EnableScheduling
public class FTPGroupTask {

    @Autowired
    private ESearch eSearch;
    @Autowired
    private NewCrowdMapper newCrowdMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UpdateUserGroupSize updateUserGroupSize;

    /**
     * 仅短信运营
     */
    @Scheduled(initialDelay = 3000, fixedDelay = 1000 * 60)
    public void onlySMS() {
        // 查询文件上传和FTP导入创建的人群
        log.info("查询文件上传和FTP导入创建的人群-仅短信运营");
        Map<String, String> onlySMSMap = new HashMap<>();
        onlySMSMap.put("onlySMS", "1");
        HashMap<String, Object> group = newCrowdMapper.queryFileGroup(onlySMSMap);
        if (null == group || null == group.get("groupId") || "".equals(String.valueOf(group.get("groupId")))) {
            log.info("当前无待执行的导入人群-仅短信运营");
            return;
        }
        String groupID = String.valueOf(group.get("groupId"));
        log.info("人群ID:{}", groupID);
        // redis key
        String _phone = groupID + "-phone";
        String _imei = groupID + "-imei";
        String _uid = groupID + "-uid";
        // 更新人群状态至更新中
        Map<String, Object> params = new HashMap<>();
        params.put("createStatus", UserGroupConstants.UPDATING);
        params.put("groupId", groupID);
        log.info("人群开始导入:{}", groupID);
        newCrowdMapper.updateCrowd(params);
        // 清理redis历史人群信息
        redisTemplate.unlink(_phone);
        redisTemplate.unlink(_imei);
        redisTemplate.unlink(_uid);
        // 文件类型：1-手机号；2-IMEI/IDFA；3-UID；
        String fileType = String.valueOf(group.get("fileImportType"));
        log.info("文件类型【1-手机号；2-IMEI/IDFA；3-UID；】:{}", fileType);
        // 文件路径
        String filePath = String.valueOf(group.get("fileRename"));
        log.info("文件路径:{}", filePath);
        List<String> area = new ArrayList<>();
        List<String> list = new ArrayList<>();
        // 查询静态标签
        Map<String, Object> labelMap = new HashMap<>();
        labelMap.put("groupID", groupID);
        Map<String, Object> label = newCrowdMapper.queryUserFtpDetail(labelMap);
        Boolean IsAllow = IsAllow(label);
        if (IsAllow) {
            // 省市
            log.info("省市范围{}", label.get("areaName"));
            area = Arrays.asList(String.valueOf(label.get("areaName")).split(","));
            // 进行号段匹配
            Map<String, Object> maps = new HashMap<>();
            maps.put("list", area);
            list = newCrowdMapper.headNumberList(maps);
        }
        // 读取文件内容
        List<String> contents = FileRW.contents(filePath);
        if (null == contents || contents.size() == 0) {
            params.clear();
            params.put("groupId", groupID);
            params.put("createStatus", UserGroupConstants.IMPORT_FAILED);
            params.put("remark", "文件内容异常");
            log.info("文件内容异常:{}", filePath);
            newCrowdMapper.updateCrowd(params);
            // 更新使用该人群的待进行策略信息
            updateStrategyInfo(groupID);
            return;
        }
        // 导入规模
        int size = contents.size();
        // 建立Redis连接
        BoundSetOperations boundSetOpsMSISDN = unlinkAndGetConnet(redisTemplate, _phone, 0);//redisTemplate.boundSetOps(_phone);
        // 反查-标签比对-数据入库
        int msisdnSize = 0;
        for (String content : contents) {
            if (!PatternVer.isMsisdn(content.trim())) {
                continue;
            }
            if (CollectionUtils.isNotEmpty(list)) {
                if (!IsHave(list, content.trim().substring(0, 7))) {
                    continue;
                }
            }
            msisdnSize++;
            boundSetOpsMSISDN.add(content.trim());
        }
        log.info("msisdns长度:{},ftpSize:{}", msisdnSize, size);
        updateUserGroupSize.update(groupID, 1, size);
        log.info("更新完成:{}", groupID);
        return;
    }

    /**
     * 人群创建失败时，更新待使用的策略信息
     *
     * @param groupId 人群id
     */
    private void updateStrategyInfo(String groupId) {
        // 人群创建失败，查询待使用的策略是否引用该人群，修改策略状态
        updateStrategy(groupId, newCrowdMapper, log);
    }

    static void updateStrategy(String groupId, NewCrowdMapper newCrowdMapper, Logger log) {
        List<Map<String, Object>> strategyList = newCrowdMapper.queryStrategyByGroupId(groupId);
        if (strategyList != null && strategyList.size() > 0) {
            int count = 0;
            for (Map<String, Object> strategy : strategyList) {
                // 策略状态改为已驳回
                strategy.put("executeStatus", 2);
                strategy.put("approverId", "000");
                strategy.put("approverName", "admin");
                // 审核状态改为已驳回
                strategy.put("approveStatus", 3);
                strategy.put("approveDesc", "人群导入失败");
                // 修改策略信息
                newCrowdMapper.updateApproveInfo(strategy);
                // 增加审批记录
                newCrowdMapper.createApproveByMap(strategy);
                count++;
            }
            log.info("已将{}条策略驳回", count);
        } else {
            log.info("当前无待进行的策略使用该人群：{}", groupId);
        }
    }

    @Scheduled(initialDelay = 5000, fixedDelay = 1000 * 60)
    public void NotOnlySms() {
        // 查询文件上传和FTP导入创建的人群
        log.info("查询文件上传和FTP导入创建的人群-非仅短信运营");
        Map<String, String> onlySMSMap = new HashMap<>();
        onlySMSMap.put("onlySMS", "0");
        HashMap<String, Object> group = newCrowdMapper.queryFileGroup(onlySMSMap);
        if (null == group || null == group.get("groupId") || "".equals(String.valueOf(group.get("groupId")))) {
            log.info("当前无待执行的导入人群");
            return;
        }
        String groupID = String.valueOf(group.get("groupId"));
        log.info("人群ID:{}", groupID);
        // 更新人群状态至更新中
        Map<String, Object> params = new HashMap<>();
        params.put("createStatus", UserGroupConstants.UPDATING);
        params.put("groupId", groupID);
        log.info("人群开始导入:{}", groupID);
        newCrowdMapper.updateCrowd(params);
        // 文件类型：1-手机号；2-IMEI/IDFA；3-UID；
        String fileType = String.valueOf(group.get("fileImportType"));
        log.info("文件类型【1-手机号；2-IMEI/IDFA；3-UID；】:{}", fileType);
        // 文件路径
        String filePath = String.valueOf(group.get("fileRename"));
        log.info("文件路径:{}", filePath);
        // 仅用于短信运营：0-否；1-是；
        String onlySMS = String.valueOf(group.get("onlySms"));
        log.info("是否仅用于短信运营【0-否；1-是；】:{}", onlySMS);
        // 读取文件内容
        List<String> contents = FileRW.contents(filePath);
        if (null == contents || contents.size() == 0) {
            params.clear();
            params.put("groupId", groupID);
            params.put("createStatus", UserGroupConstants.IMPORT_FAILED);
            params.put("remark", "文件内容异常");
            log.info("文件内容异常:{}", filePath);
            newCrowdMapper.updateCrowd(params);
            // 更新使用该人群的待进行策略信息
            updateStrategyInfo(groupID);
            return;
        }
        // 导入规模
        int size = contents.size();
        // 筛选人群条件判断  查询静态标签
        Map<String, Object> labelMap = new HashMap<>();
        labelMap.put("groupID", groupID);
        Map<String, Object> label = Optional.ofNullable(newCrowdMapper.queryUserFtpDetail(labelMap)).orElse(new HashMap<>());
        // 判断是否有静态标签
        Boolean IsCheck = IsCheck(label);
        log.info("判断是否有静态标签:{}", IsCheck);
        // 有静态标签时 1.反差 2筛选条件  无静态标签时 1.反查 入库 2.反查不到也要入
        // redis key
        String _phone = groupID + "-phone";
        String _imei = groupID + "-imei";
        String _uid = groupID + "-uid";
        // 建立Redis连接
        BoundSetOperations boundSetOpsMSISDN = unlinkAndGetConnet(redisTemplate, _phone, 0);
        BoundSetOperations boundSetOpsIMEI = unlinkAndGetConnet(redisTemplate, _imei, 0);
//        redisTemplate.boundSetOps(_uid);
        BoundSetOperations boundSetOpsUID = unlinkAndGetConnet(redisTemplate, _uid, 0);
        // 更新人群状态，回填人群规模
        params.clear();
        params.put("groupId", groupID);
        Map<String, Object> result;
        //  有静态标签筛选后的结果集
        Set<String> uids = new HashSet<>();
        Set<String> imeis = new HashSet<>();
        Set<String> msisdns = new HashSet<>();
        for (String content : contents) {
            // 去除空文件
            if (StringUtils.isBlank(content)) {
                continue;
            }
            result = filterMap(label, content.trim(), fileType, IsCheck);
            if (null == result) {
                continue;
            }
            // 反查结果
            if (null != result.get("uids")) {
                uids = (Set<String>) result.get("uids");
            }
            if (null != result.get("imeis")) {
                imeis = (Set<String>) result.get("imeis");
            }
            if (null != result.get("msisdns")) {
                msisdns = (Set<String>) result.get("msisdns");
            }
            // 非仅短信运营人群录入设备号和uid
            if ("0".equals(onlySMS)) {
                if (null != uids && uids.size() != 0) {
                    for (String uid : uids) {
                        boundSetOpsUID.add(uid.trim());
                    }
                }
                if (null != imeis && imeis.size() != 0) {
                    for (String imei : imeis) {
                        if (!"00000000-0000-0000-0000-000000000000".equals(imei.trim())) {
                            boundSetOpsIMEI.add(imei.trim());
                        }
                    }
                }
            }
            if (null != msisdns && msisdns.size() != 0) {
                for (String msisdn : msisdns) {
                    boundSetOpsMSISDN.add(msisdn.trim());
                }
            }
        }
        log.info("uids长度:{},imeis长度:{},msisdns长度:{},ftpSize:{}", uids.size(), imeis.size(), msisdns.size(), size);
        updateUserGroupSize.update(groupID, 1, size);
        log.info("更新完成:{}", groupID);
        return;
    }

    private BoundSetOperations unlinkAndGetConnet(RedisTemplate redisTemplate, String key, Integer num) {
        try {
            redisTemplate.unlink(key);
            BoundSetOperations boundSetOps = redisTemplate.boundSetOps(key);
            return boundSetOps;
        } catch (Exception e) {
            try {
                Thread.sleep(60000L);
            } catch (InterruptedException ex) {
                log.info("主线程睡眠异常" + ex.getMessage());
            }
            num = num++;
            log.info("递归次数：" + num + "，key：" + key + "连接redis异常：" + e.getMessage());
            return unlinkAndGetConnet(redisTemplate, key, num);
        }

    }

    /**
     * 判断是否勾选了静态标签(包括性别 年龄 省市 手机等)
     *
     * @param map
     * @return Boolean值 为ture 勾选 为false 未勾选
     */
    private Boolean IsCheck(Map<String, Object> map) {
        if (map.get("age") != null && !"".equals(map.get("age"))) {
            return true;
        }
        if (map.get("sex") != null && !"".equals(map.get("sex"))) {
            return true;
        }
        if (map.get("areaName") != null && !"".equals(map.get("areaName"))) {
            return true;
        }
        if (map.get("phoneSystem") != null && !"".equals(map.get("phoneSystem"))) {
            return true;
        }
        return false;
    }


    /**
     * 对应的人群
     *
     * @param label
     * @param content
     * @param fileType
     * @return
     */
    private Map<String, Object> filterMap(Map<String, Object> label, String content, String fileType, Boolean IsCheck) {
        List<String> age = new ArrayList<>();
        List<String> sex = new ArrayList<>();
        List<String> area = new ArrayList<>();
        List<String> system = new ArrayList<>();
        if (label.get("age") != null && !"".equals(label.get("age"))) {
            log.info("年龄范围{}", label.get("age"));
            age = Arrays.stream(label.get("age").toString().split("&")).collect(Collectors.toList());
        }
        // 性别
        if (label.get("") != null && !"".equals(label.get("age"))) {
            log.info("性别范围{}", label.get("sex"));
            sex = Arrays.asList(label.get("sex").toString().split(","));
        }
        // 省市
        if (label.get("areaName") != null && !"".equals(label.get("areaName"))) {
            log.info("省市范围{}", label.get("areaName"));
            area = Arrays.asList(String.valueOf(label.get("areaName")).split(","));
        }
        // 操作系统
        if (label.get("phoneSystem") != null && !"".equals(label.get("phoneSystem"))) {
            log.info("操作系统范围{}", label.get("phoneSystem"));
            system = Arrays.asList(label.get("phoneSystem").toString().split(","));
        }
        // 1.反差ES 得到的人群集
        return IsCheck == true ? eSearch.analysis(content, fileType, "0", age, sex, system, area) : eSearch.analysis2(content, fileType, "0", age, sex, system, area);
    }

    /**
     * 判断str 是否存在于list 存在返回true
     *
     * @param list
     * @param str
     * @return
     */
    private Boolean IsHave(List<String> list, String str) {
        return list.stream().filter(m -> str.equals(m)).findAny().isPresent();
    }

    /**
     * @param label
     * @return
     */
    private Boolean IsAllow(Map<String, Object> label) {
        if (!(null == label || null == label.get("groupId") || "".equals(String.valueOf(label.get("groupId"))))) {
            // 省市
            if (null != label.get("areaName") && !"".equals(String.valueOf(label.get("areaName")))) {
                log.info("省市范围{}", label.get("areaName"));
                return true;
            }
        }
        return false;
    }
}
